package com.wang.tool.monitor.core;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.wang.tool.common.ErrorInfo;
import com.wang.tool.monitor.LinkLogUtil;
import com.wang.tool.monitor.sample.GlobalLinkSampler;
import com.wang.tool.monitor.template.MonitorInvoker;
import com.wang.tool.monitor.translator.ErrorTranslator;
import com.wang.tool.util.CommonUtil;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Predicate;
import java.util.stream.Collectors;

/**
 * 链路信息
 *
 * @Author wangshaopeng
 * @Date 2022/7/1
 */
public class LinkInfo implements Comparable<LinkInfo> {

    public static final String TAG_SERIALIZE_PROFILER_ERROR = "SerializeProfilerError";
    public static final String TAG_SERIALIZE_LINK_ERROR = "SerializeLinkError";
    /**
     * 监控信息 非空
     */
    private transient final MonitorInfo<?> monitor;

    /**
     * 根节点
     * 非空 自己是跟则指向自己
     */
    private transient final LinkInfo root;

    /**
     * 序列
     * 父序列
     */
    private transient final AtomicInteger sequence;

    /**
     * 是否命中采样
     * 非空，全部都copy from toot节点
     */
    private transient final boolean sampling;

    /**
     * id
     * 一个链路树下唯一
     * 根部是0
     */
    private final String linkid;

    /**
     * 分组 非空
     */
    private final String group;


    /**
     * 双码-业务码 非空
     */
    private final String biz;

    /**
     * 双码-动作码 非空
     */
    private final String action;

    /**
     * 爹节点
     * 爹节点为空代表自己为根节点
     */
    private transient final LinkInfo parent;

    /**
     * 回写线程的链路信息
     * 通常为parent,可手动指定
     */
    private transient final LinkInfo wbLink;


    /**
     * 是否需要debug
     * 上游节点判别,下链路可用可自定义
     */
    private transient boolean debug;

    /**
     * 本节点开始时间
     */
    private final transient long beginTime;

    /**
     * 结束时间
     * 子节点会拿此值判断是否超时现象
     */
    private volatile transient long endTime = -1;

    /**
     * 耗时
     * 不用weaver赋值
     */
    private int cost = -1;

    /**
     * 性能指标 逻辑上非空
     * 默认为超时
     * 当结果来临时再改成正常/慢调用
     */
    private PerformanceEnum performance = PerformanceEnum.TIMEOUT;


    /**
     * 异常信息
     *
     * @see ErrorTranslator
     */
    private ErrorInfo error;

    /**
     * 是否存在隐形异常
     * 1.使用InvokeTemplate抓取了异常
     * 2.输出了异常日志
     * 3.手动标记
     *
     * @see MonitorInvoker#handleError
     * @see LinkLogUtil#error
     */
    private boolean implicitError;

    /**
     * 是否疑似慢调用
     * 1.自己为slow/time_out会触发自己及上游的疑似慢
     */
    private boolean implicitSlow;


    /**
     * 状态码
     * 对结果的一种描述
     * weaver主要赋值处
     */
    private String status;


    /**
     * 根开始后多久,此节点开始执行的
     */
    private final int begin;

    /**
     * 根开始后多久,此节点结束的
     */
    private int end = -1;

    /**
     * weaver主要赋值处
     * 自定义摘要信息
     * 为了解决不同层的函数出入参模型不一致及想要记录的核心参数不一致而设计的扩展点
     * 不同函数可以自定义织入器织入自己需要监控的核心标记,由MonitorWriter统一输出
     */
    private List<Digest> digests;

    /**
     * 标签
     */
    private List<String> tags;

    /**
     * 子节点
     * id-链路数据
     */
    private final Map<String, LinkInfo> segments = new ConcurrentHashMap<>(4);

    /**
     * 简讯数据，链路数据会直接输出，不宜过大
     * 自由扩展
     * 例如：结果列表可能比较大，这时我简单记录下数据量是可以的，或者记录下结果的核心key
     *
     * @see LinkInfo#addShortDetail
     * @see LinkInfo#addShortDetailOnDebug
     */
    private Map<String, Object> shortDetails;

    /**
     * 添加摘要
     *
     * @param name
     * @param val
     * @return
     */
    public Digest addDigest(String name, Object val) {
        this.digests = this.digests == null ? new ArrayList<>() : this.digests;
        Digest digest = new Digest(name == null ? "" : name, val == null ? LinkLogUtil.EMPTY_PLACEHOLDER : val.toString());
        this.digests.add(digest);
        return digest;
    }

    /**
     * 添加标签
     *
     * @param tag
     * @param v1
     * @param v2
     */
    public void addTag(String tag, String v1, String v2) {
        //函数内部会调用,多线程线程安全问题忽略,可能性应该极小
        this.tags = this.tags == null ? new ArrayList<>() : this.tags;
        this.tags.add((tag == null ? LinkLogUtil.EMPTY_PLACEHOLDER : tag) + ':' + (v1 == null ? LinkLogUtil.EMPTY_PLACEHOLDER : v1) + ':' + (v2 == null ? LinkLogUtil.EMPTY_PLACEHOLDER : v2));
    }

    /**
     * 简短详情中设置debug信息
     *
     * @param key
     * @param val
     */
    public void addShortDetailOnDebug(String key, Object val) {
        if (!debug) {
            return;
        }
        val = val == null ? LinkLogUtil.EMPTY_PLACEHOLDER : val;
        this.shortDetails = this.shortDetails == null ? new LinkedHashMap<>(4) : this.shortDetails;
        this.shortDetails.put(key, val);
    }


    /**
     * 简短详情中设置信息
     *
     * @param key
     * @param val
     */
    public void addShortDetail(String key, Object val) {
        val = val == null ? LinkLogUtil.EMPTY_PLACEHOLDER : val;
        this.shortDetails = this.shortDetails == null ? new LinkedHashMap<>(4) : this.shortDetails;
        this.shortDetails.put(key, val);
    }


    public String bizAction() {
        return biz + "#" + action;
    }


    /**
     * 修改debug状态
     * @param debug
     */
    public void markDebug(boolean debug) {
//        取消debug的时候仅限自己
        if(!debug || this.debug){
            this.debug = debug;
            return;
        }
//        当前debug=false && 设置为true，向上 & 向下 扩散
        this.debug = true;
        if(parent!=null){
            parent.markDebug(true);
        }
        for (LinkInfo child : segments.values()) {
            child.markDebug(true);
        }
    }

    /**
     * 递归标记可能存在异常
     */
    public void markImplicitError() {
        if(this.implicitError){
            return;
        }
        this.implicitError = true;
        if (parent != null) {
            parent.markImplicitError();
        }
    }


    /**
     * 递归标记可能存在慢调用
     */
    public void markImplicitSlow() {
        if(this.implicitSlow){
            return;
        }
        this.implicitSlow = true;
        if (parent != null) {
            parent.markImplicitSlow();
        }
    }


    /**
     * 判断有没有抓取异常
     * 1.使用InvokeTemplate抓取了异常
     * 2.子节点有异常但是自己没异常
     * @return
     */
    public boolean judgeIsCatchError(){
        //内部无异常 或抛出异常，不可能会catch异常
        if(!isImplicitError() || getError()!=null){
            return false;
        }
//        implicitError=true && error==null
//        没子则代表自己标记的
        if (segments.isEmpty()) {
            return true;
        }

//      子节点没有隐含异常
        if((segments.values().stream().allMatch(new Predicate<LinkInfo>() {
            @Override
            public boolean test(LinkInfo linkInfo) {
                return !linkInfo.isImplicitError();
            }
        }))){
            return true;
        }

//        子节点有超时的
        if((segments.values().stream().anyMatch(new Predicate<LinkInfo>() {
            @Override
            public boolean test(LinkInfo linkInfo) {
                return linkInfo.getError()!=null;
            }
        }))){
            return true;
        }

        return false;
    }


    public String getLinkid() {
        return linkid;
    }

    public String getGroup() {
        return group;
    }


    public String getBiz() {
        return biz;
    }

    public String getAction() {
        return action;
    }

    public long getBeginTime() {
        return beginTime;
    }


    public long getEndTime() {
        return endTime;
    }


    public int getBegin() {
        return begin;
    }

    public int getEnd() {
        return end;
    }

    public int getCost() {
        return cost;
    }


    public String getStatus() {
        return status;
    }

    public Map<String, Object> getShortDetails() {
        return shortDetails;
    }


    public ErrorInfo getError() {
        return error;
    }

    public LinkInfo getRoot() {
        return root;
    }

    public LinkInfo getParent() {
        return parent;
    }

    public Map<String, LinkInfo> getSegments() {
        return segments;
    }

    public boolean isDebug() {
        return debug;
    }

    public AtomicInteger getSequence() {
        return sequence;
    }

    public PerformanceEnum getPerformance() {
        return performance;
    }

    public List<Digest> getDigests() {
        return digests;
    }

    public List<String> getTags() {
        return tags;
    }

    public void setStatus(String status) {
        this.status = status;
    }

    public MonitorInfo<?> getMonitor() {
        return monitor;
    }

    /**
     * 是否疑似慢调用
     * 1.自己为slow/time_out会触发自己及上游的疑似慢
     * @return
     */
    public boolean isImplicitSlow() {
        return implicitSlow;
    }

    /**
     * 是否存在隐形异常
     * 1.使用InvokeTemplate抓取了异常
     * 2.输出了异常日志
     * 3.手动标记
     * @return
     */
    public boolean isImplicitError() {
        return implicitError;
    }

    /**
     * 是否命中采样率
     * @return
     */
    public boolean isSampling() {
        return sampling;
    }

    /*-------------------------分割线-------------------------*/


    /**
     * 根据当前段信息创建一个自己的段,并将自己当做一个参与者参与父段的链路统计
     *
     * @param monitor
     * @param wbLink
     * @param parent
     * @param group
     * @param biz
     * @param action
     * @param beginTime
     */
    LinkInfo(MonitorInfo<?> monitor, LinkInfo wbLink, LinkInfo parent, String group, String biz, String action, long beginTime) {
        this.monitor = monitor;
        this.group = group;
        this.biz = biz;
        this.action = action;
        this.parent = parent;
        this.wbLink = wbLink;
        this.beginTime = beginTime;
        if (this.parent == null) {
            this.root = this;
            this.sequence = new AtomicInteger(0);
            this.linkid = "0";
            this.sampling = GlobalLinkSampler.isSampling(group, biz, action, beginTime);
            this.debug = false;
            this.begin = 0;
        } else {
            this.root = this.parent.root;
            this.sequence = this.root.sequence;
            this.sampling = this.root.sampling;
            this.linkid = this.parent.linkid + "." + this.sequence.incrementAndGet();
            //拷贝自爹
            this.debug = this.parent.debug;
            //写入爹
            this.parent.segments.put(this.linkid, this);
            this.begin = (int) (beginTime - this.root.beginTime);
        }
    }


    void setUpLink() {
        LinkHolder.setCurrent(this);
    }


    /**
     * 恢复线程链路
     */
    void recoverLink() {
        LinkHolder.setCurrent(wbLink);
    }


    /**
     * 填充结果
     *
     * @param error    异常信息
     * @param endTime  结束时间
     * @param slowTime 慢调用阈值
     * @param outTime  超时调用阈值
     */
    void end(ErrorInfo error, long endTime, Integer slowTime, Integer outTime) {
        this.error = error;
        this.endTime = endTime;
        this.cost = (int) (endTime - beginTime);
        this.end = this.begin + this.cost;

        /**
         * 计算性能指标
         */
        if ((parent != null && parent.endTime != -1 && parent.endTime < this.endTime) || (outTime != null && cost > outTime)) {
            //爹完成了或者超过了超时时间(外放接口处设置)不严格计较严谨性
            this.performance = PerformanceEnum.TIMEOUT;
            markImplicitSlow();
        } else if (slowTime != null && cost > slowTime) {
            //普通慢
            this.performance = PerformanceEnum.SLOW;
            markImplicitSlow();
        } else {
            //正常
            this.performance = PerformanceEnum.NORMAL;
            if(segments.values().stream().anyMatch(new Predicate<LinkInfo>() {
                @Override
                public boolean test(LinkInfo linkInfo) {
                    return linkInfo.getPerformance()==PerformanceEnum.TIMEOUT;
                }
            })) {
                markImplicitSlow();
            }
        }

        String status = getStatus();
        //状态码及标记
        if (error != null) {
            setStatus(error.getErrorCode());
            //上报异常
            markImplicitError();
            monitor.addDetail(MonitorInfo.ERROR, error);
        } else {
            Object result = monitor.getResult();
            if (result == null) {
                setStatus(MonitorInfo.NULL);
            } else if (result instanceof Collection) {
                int size = ((Collection<?>) result).size();
                if (size == 0) {
                    setStatus(MonitorInfo.EMPTY);
                } else {
                    setStatus(MonitorInfo.OK);
                    monitor.addTag("arraySize", String.valueOf(size), null);
                }
            } else if (result instanceof Map) {
                int size = ((Map<?, ?>) result).size();
                if (size == 0) {
                    setStatus(MonitorInfo.EMPTY);
                } else {
                    setStatus(MonitorInfo.OK);
                    monitor.addTag("mapSize", String.valueOf(size), null);
                }
            } else {
                setStatus(MonitorInfo.OK);
            }
        }
//        预设状态
        if (status != null) {
            addTag(MonitorInfo.TAG_PRE_SET_STATUS, status, getStatus());
            setStatus(status);
        }
    }

    /*-------------------------分割线-------------------------*/


    /**
     * 性能表
     *
     * @return
     */
    public List<String> profiler() {
        int i = 0;
        do {
            try {
                //在异步场景,有节点又加入了linkInfo时可能会报错
                //所以这里最多重试3次
                List<String> links = new ArrayList<>();
                appendProfiler(links);
                return links;
            } catch (Throwable e) {
                LinkLogUtil.newTagLog(TAG_SERIALIZE_PROFILER_ERROR)
                        .pointLink(this)
                        .addAttachment(MonitorInfo.ERROR, e)
                        .log();
            }
        } while (++i <= 3);
        List<String> links = new ArrayList<>();
        links.add("serializeProfilerError");
        return links;
    }

    private void appendProfiler(List<String> links) {
        //id group#biz#action(Y,status,performance,begin,cost)da=v1|db=v2
        //未结束
        String profiler;
        if (cost < 0) {
            profiler = linkid + " " + group + "#" + biz + "#" + action + "(" + (implicitError ? "I" : "-") + ",-," + performance.name() + "," + begin + ",-)";
        } else {
            profiler = linkid + " " + group + "#" + biz + "#" + action + "(" + (error == null ? (implicitError ? "I" : "Y") : "N") + "," + (status == null ? "-" : status) + "," + (performance==PerformanceEnum.NORMAL && implicitSlow?"I":performance.name()) + "," + begin + "," + cost + ")";
        }
        if (digests != null) {
            profiler += CommonUtil.join(digests, ";");
        }
        links.add(profiler);
        if (segments.isEmpty()) {
            return;
        }
        List<LinkInfo> segments = this.segments.values().stream().sorted().collect(Collectors.toList());
        for (LinkInfo segment : segments) {
            segment.appendProfiler(links);
        }
    }

    /*-------------------------分割线-------------------------*/

    /**
     * 格式化链路json
     * 输出的信息被使用图化html图化展示
     *
     * @return
     * @see horse_up_down_class.html
     */
    @Override
    public String toString() {
        int i = 0;
        do {
            try {
                //在异步场景,有节点又加入了linkInfo时可能会报错
                //所以这里最多重试3次
                return JSON.toJSONString(this, SerializerFeature.WriteNonStringKeyAsString);
            } catch (Throwable e) {
                LinkLogUtil.newTagLog(TAG_SERIALIZE_LINK_ERROR)
                        .pointLink(this)
                        .addAttachment(MonitorInfo.ERROR, e)
                        .log();
            }
        } while (++i <= 3);
        return "{\"serializeLinkError\":\"try3Times\"}";
    }

    /**
     * 性能表字符串
     * 可以理解为简化版链路信息
     * 输出的信息被使用图化html图化展示
     *
     * @return
     * @see horse_up_down_class.html
     */
    public String toProfilerString() {
        return CommonUtil.join(profiler(), "$M$");
    }


    @Override
    public int compareTo(LinkInfo o) {
        //数字比.要大,可以这么比
        return linkid.compareTo(o.linkid);
    }

}
